package com.commonlib.downloadmgr.base;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Set;

import android.content.Context;
import android.content.Intent;

public abstract class DownloadBaseManager<T>{
	private static final String TAG = DownloadBaseManager.class.getSimpleName();
	private int MAX_TASK = 2; //同时下载任务上限
	private ArrayList<DownloadBaseJob<T>> mAllJobs;
	private ArrayList<DownloadBaseJob<T>> mActiveJobs;
	private LinkedList<DownloadBaseJob<T>> mWaitingJobs;
	private ArrayList<DownloadBaseJob<T>> mFinishedJobs;
	protected Context context;
	
	
	protected DownloadBaseManager(Context context, int maxTask) {
		this.context = context;
		this.MAX_TASK = maxTask < 1 ? 1 : (maxTask > 10 ? 10 : maxTask);
		initJobs();
	}
	
	private void initJobs(){
		Log.v(TAG, "initJobs");
		mAllJobs = readData();
		mActiveJobs = new ArrayList<DownloadBaseJob<T>>();
		mWaitingJobs = new LinkedList<DownloadBaseJob<T>>();
		mFinishedJobs = new ArrayList<DownloadBaseJob<T>>();
		if (mAllJobs == null) {
			mAllJobs = new ArrayList<DownloadBaseJob<T>>();
		}
		for (DownloadBaseJob<T> job : mAllJobs) {
			if (DownloadBaseJob.STATE_DOWNLOADING == job.getState() || DownloadBaseJob.STATE_PAUSED == job.getState()){
				if (mActiveJobs.size() >= MAX_TASK) {
					job.setState(DownloadBaseJob.STATE_IDLE, true);
					mWaitingJobs.add(job);
				}else{
					mActiveJobs.add(job);
				}
			}else if(DownloadBaseJob.
					STATE_IDLE == job.getState()){
				mWaitingJobs.add(job);
			}else{
				mFinishedJobs.add(job);
			}
		}
		Intent intent = getIntentDownoadService();
		intent.setAction(DownloadBaseService.ACTION_HANDLE_EXCEPTION);
		context.startService(intent);
	}
	
	/**
	 * add a download job to the download queue
	 * @param job
	 */
	public synchronized void addDownloadJob(DownloadBaseJob<T> job) {
		Log.v(TAG, "addDownloadJob");
		if (job == null) {
			Log.w(TAG, "job is null");
			return;
		}
		boolean needStart = false;
		boolean isExisted = false;
		if (getDownloadJob(job.getId()) != null) {
			Log.w(TAG, "duplex add job");
			isExisted = true;
			int mState = getDownloadJob(job.getId()).getState();
			if (DownloadBaseJob.STATE_FAILED == mState || DownloadBaseJob.STATE_IDLE == mState) {
				needStart = true;
				removeDownloadJob(job.getId(), true, false);
			}
		}
			
		if (isExisted && !needStart) {
			Log.w(TAG, "job is duplex and needn't to restart");
			return;
		}
		if (!isExisted) {
			mAllJobs.add(job);
		}
		if (mActiveJobs.size() < MAX_TASK) {
			mActiveJobs.add(job);
			needStart = true;
		}else{
			job.setState(DownloadBaseJob.STATE_IDLE, true);
			mWaitingJobs.add(job);
			needStart = false;
		}
		if (needStart) {
			Intent intent = getIntentDownoadService();
			intent.setAction(DownloadBaseService.ACTION_ADD_TO_DOWNLOAD);
			intent.putExtra(DownloadBaseService.EXTRA_DOWNLOADJOB_ID, job.getId());
			context.startService(intent);
		}
	}

	/**
	 * add some new download jobs to the download queue
	 * @param jobs
	 */
	public synchronized void addDownloadJobs(ArrayList<DownloadBaseJob<T>> jobs){
		//TODO
	}
	
	/**
	 * remove a download job by id
	 * @param id
	 * @param isForTransfer if just for transfering job among collections, and keep the job in mAllJobs
	 * @param isDeleteFile if delete local download file
	 * @return
	 */
	public synchronized boolean removeDownloadJob(String id, boolean isForTransfer, boolean isDeleteFile) {
		Log.v(TAG, "removeDownloadJob, job id:" + id + ", isForTransfer:" + isForTransfer + ", isDeleteFile:" + isDeleteFile);
		boolean removed = false;
		DownloadBaseJob<T> removedJob = null;
		if (!removed) {
			for (DownloadBaseJob<T> job : mActiveJobs) {
				if (job.getId().equals(id)) {
					mActiveJobs.remove(job);
					removed = true;
					removedJob = job;
					break;
				}
			}
		}
		if (!removed) {
			for (DownloadBaseJob<T> job : mWaitingJobs) {
				if (job.getId().equals(id)) {
					mWaitingJobs.remove(job);
					removed = true;
					removedJob = job;
					break;
				}
			}
		}
		if (!removed) {
			for (DownloadBaseJob<T> job : mFinishedJobs) {
				if (job.getId().equals(id)) {
					mFinishedJobs.remove(job);
					removed = true;
					removedJob = job;
					break;
				}
			}
		}
		if (!isForTransfer) {
			for (DownloadBaseJob<T> job : mAllJobs) {
				if (job.getId().equals(id)) {
					mAllJobs.remove(job);
					removed = true;
					removedJob = job;
					break;
				}
			}
		}
		if (isDeleteFile && removedJob != null) {
			removedJob.getmHelper().deleteDownloadedFile(context, removedJob);
		}
		return removed;
	}

	/**
	 * remove a download job by id
	 * @param ids
	 * @param isForTransfer if just for transfering job among collections, and keep the job in mAllJobs
	 * @param isDeleteFile if delete local download file
	 * @return
	 */
	public synchronized void removeDownloadJobs(Set<String> ids, boolean isForTransfer, boolean isDeleteFile) {
		Log.v(TAG, "removeDownloadJobs, jobs id:" + ids.toString());
		ArrayList<DownloadBaseJob<T>> removedJobs = new ArrayList<DownloadBaseJob<T>>();
		for (DownloadBaseJob<T> job : mActiveJobs) {
			if (ids.contains(job.getId())) {
				removedJobs.add(job);
			}
		}
		mActiveJobs.removeAll(removedJobs);
		if (isDeleteFile) {
			for (DownloadBaseJob<T> job : removedJobs) {
				job.getmHelper().deleteDownloadedFile(context, job);
			}
		}
		
		removedJobs.clear();
		for (DownloadBaseJob<T> job : mWaitingJobs) {
			if (ids.contains(job.getId())) {
				removedJobs.add(job);
			}
		}
		mWaitingJobs.removeAll(removedJobs);
		if (isDeleteFile) {
			for (DownloadBaseJob<T> job : removedJobs) {
				job.getmHelper().deleteDownloadedFile(context, job);
			}
		}
		
		removedJobs.clear();
		for (DownloadBaseJob<T> job : mFinishedJobs) {
			if (ids.contains(job.getId())) {
				removedJobs.add(job);
			}
		}
		mFinishedJobs.removeAll(removedJobs);
		if (isDeleteFile) {
			for (DownloadBaseJob<T> job : removedJobs) {
				job.getmHelper().deleteDownloadedFile(context, job);
			}
		}
		
		if (!isForTransfer) {
			removedJobs.clear();
			for (DownloadBaseJob<T> job : mAllJobs) {
				if (ids.contains(job.getId())) {
					removedJobs.add(job);
				}
			}
			mAllJobs.removeAll(removedJobs);
			if (isDeleteFile) {
				for (DownloadBaseJob<T> job : removedJobs) {
					job.getmHelper().deleteDownloadedFile(context, job);
				}
			}
		}
	}
	
	/**
	 * get all download jobs
	 * @return
	 */
	public synchronized ArrayList<DownloadBaseJob<T>> getAllJobs() {
//		mAllJobs.clear();
//		mAllJobs.addAll(mActiveJobs);
//		mAllJobs.addAll(mWaitingJobs);
//		mAllJobs.addAll(mFinishedJobs);
		return mAllJobs;
	}
	
	/**
	 * get active jobs
	 * @return
	 */
	public synchronized ArrayList<DownloadBaseJob<T>> getActiveJobs(){
		return mActiveJobs;
	}
	
	/**
	 * get waiting jobs
	 * @return
	 */
	public synchronized LinkedList<DownloadBaseJob<T>> getWaitingJobs() {
		return mWaitingJobs;
	}

	/**
	 * get finished jobs
	 * @return
	 */
	public synchronized ArrayList<DownloadBaseJob<T>> getFinishJobs() {
		return mFinishedJobs;
	}
	
	/**
	 * get download job by id
	 * @param id
	 * @return may be null
	 */
	public synchronized DownloadBaseJob<T> getDownloadJob(String id) {
		for(DownloadBaseJob<T> downloadJob : getAllJobs()){
			if (downloadJob.getId().equals(id)) {
				return downloadJob;
			}
		}
		return null;
	}
	
	/**
	 * get download jobs by job ids
	 * @param ids
	 * @return
	 */
	public synchronized ArrayList<DownloadBaseJob<T>> getDownloadJobs(Set<String> ids){
		ArrayList<DownloadBaseJob<T>> retval = new ArrayList<DownloadBaseJob<T>>();
		for(DownloadBaseJob<T> downloadJob : getAllJobs()){
			if (ids.contains(downloadJob.getId())) {
				retval.add(downloadJob);
			}
		}
		return retval;
	}
	
	/**
	 * callback this method when finished a download job
	 * @param id
	 */
	public synchronized void onFilishJob(String id){
		Log.v(TAG, "onFilishJob");
		DownloadBaseJob<T> job = getDownloadJob(id);
		if (job == null) {
			Log.e(TAG, "Job must not be null!");
			return;
		}
		if (DownloadBaseJob.STATE_SUCCESS != job.getState()
			&& DownloadBaseJob.STATE_FAILED != job.getState()){
			Log.e(TAG, "Wrong state, it is not a finishing state!");
			return;
		}
		
		removeDownloadJob(id, true, false);
		mFinishedJobs.add(job);
		
		executeWaitingJob();
		
		//当前无活跃任务，关闭下载服务
		if (mActiveJobs.isEmpty()) {
			Log.v(TAG, "no active job, stop download service");
			Intent intent = getIntentDownoadService();
			context.stopService(intent);
		}
	}
	
	public synchronized void executeWaitingJob(){
		Log.v(TAG, "executeWaitingJob");
		DownloadBaseJob<T> waitingJob = mWaitingJobs.poll();
		addDownloadJob(waitingJob);
	}
	
	/**
	 * 从本地缓存读取下载任务
	 * @return
	 */
	public abstract ArrayList<DownloadBaseJob<T>> readData();
	
	/**
	 * 更新指定的下载任务到本地缓存
	 * @param job
	 */
	public abstract long updateData(DownloadBaseJob<T> job);
	
	/**
	 * 删除指定id对应的下载任务
	 * @param id
	 * @return
	 */
	public abstract long deleteData(String id);
	
	/**
	 * 查询指定id的下载任务
	 * @param id
	 */
	public abstract DownloadBaseJob<T> queryData(String id);
	
	/**
	 * 获取，启动下载服务的intent
	 * @return
	 */
	protected abstract Intent getIntentDownoadService();
	
}
